using System.Text;
using System.Windows.Forms;
using System.Xml.XPath;

namespace Gean.Gui.WinForm.XmlTreeView
{
    public class XPathNavigatorTreeNode : TreeNode
    {
        #region Variables

        private XPathNavigator _Navigator;

        #endregion

        #region Constructors

        public XPathNavigatorTreeNode()
        {
        }

        public XPathNavigatorTreeNode(XPathNavigator navigator)
            : this(navigator, false)
        {
        }

        public XPathNavigatorTreeNode(XPathNavigator navigator, bool hasCustomChildNodes)
            : this(navigator, hasCustomChildNodes, null)
        {
        }

        public XPathNavigatorTreeNode(XPathNavigator navigator, bool hasCustomChildNodes, string customChildNodePrefix)
        {
            HasCustomChildNodes = hasCustomChildNodes;
            CustomChildNodePrefix = customChildNodePrefix;
            Navigator = navigator;
        }

        #endregion

        #region Properties

        /// <summary>
        /// Gets or sets whether this XPathNavigatorTreeNode has been expanded and loaded.
        /// </summary>
        public bool HasExpanded { get; set; }

        public bool HasCustomChildNodes { get; set; }
        public string CustomChildNodePrefix { get; set; }

        /// <summary>
        /// Gets or sets the XPathNavigator this XPathNavigatorTreeNode represents.
        /// </summary>
        public XPathNavigator Navigator
        {
            get { return _Navigator; }
            set
            {
                _Navigator = value;
                Initialize();
            }
        }

        #endregion

        #region Methods

        /// <summary>
        /// Returns the text used to display this XPathNavigatorTreeNode, formatted using the XPathNavigator it represents.
        /// </summary>
        /// <returns></returns>
        public string GetDisplayText()
        {
            if (_Navigator == null)
                return string.Empty;

            var builder = new StringBuilder();
            switch (_Navigator.NodeType)
            {
                case XPathNodeType.Comment:
                    // comments are easy, just append the value inside <!-- --> tags
                    builder.Append("<!--");
                    builder.Append(StripNonPrintableChars(_Navigator.Value));
                    builder.Append(" -->");
                    break;

                case XPathNodeType.Root:
                case XPathNodeType.Element:
                    // append the custom child node prefix, if any
                    if (!string.IsNullOrEmpty(CustomChildNodePrefix))
                        builder.Append(CustomChildNodePrefix + " ");

                    // append the start of the element
                    builder.AppendFormat("<{0}", _Navigator.Name);

                    // append any attributes
                    if (_Navigator.HasAttributes)
                    {
                        // clone the node's navigator (cursor), so it doesn't lose it's position
                        XPathNavigator attributeNavigator = _Navigator.Clone();
                        if (attributeNavigator.MoveToFirstAttribute())
                        {
                            do
                            {
                                builder.Append(" ");

                                builder.AppendFormat("{0}=\"{1}\"", attributeNavigator.Name, attributeNavigator.Value);
                            } while (attributeNavigator.MoveToNextAttribute());
                        }
                    }

                    // if the element has no children, close the node immediately
                    builder.Append(!_Navigator.HasChildren ? "/>" : ">");
                    break;

                default:
                    // all other node types are easy, just append the value
                    // strings, whitespace, etc.
                    builder.Append(StripNonPrintableChars(_Navigator.Value));
                    break;
            }
            return builder.ToString();
        }

        /// <summary>
        /// Initializes the node using it's XPathNavigator
        /// </summary>
        public void Initialize()
        {
            // default to an empty string
            string displayText = string.Empty;

            Nodes.Clear();
            if (_Navigator != null)
            {
                displayText = GetDisplayText();

                // if the xml node has children, add a placeholder node,
                // so that it can be expanded.  a treenode with no child 
                // nodes has no expansion indicator , and cannot be expanded
                if (_Navigator.HasChildren || HasCustomChildNodes)
                {
                    Nodes.Add(string.Empty);
                }
            }
            Text = displayText;
        }

        private string StripNonPrintableChars(string value)
        {
            if (string.IsNullOrEmpty(value))
                return value;

            // todo: use a regex instead
            value = value.Trim(new[] {'\r', '\n', '\t'});
            value = value.Replace("\r", null);
            value = value.Replace("\n", " ");
            value = value.Replace("\t", null);

            return value;
        }

        #endregion
    }
}